/*
 * @(#)StringUtil.java $version 2010. 8. 11
 *
 * Copyright 2007-2010 NHN Corp. All rights Reserved. 
 * NHN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.nhncorp.usf.core.util;


import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


import com.nhncorp.usf.core.config.ReplaceAllInfo;
/**
 * @author Web Platform Development Team.
 */
public class StringUtil {
	static Log log = LogFactory.getLog(StringUtil.class);
	
	/**
	 * The empty String <code>""</code>.
	 */
	public static final String EMPTY = "";

	/**
	 * Checks if is not empty.
	 * 
	 * @param obj the obj
	 * @param trim the trim
	 * 
	 * @return true, if is not empty
	 */
	public static boolean isNotEmpty(Object obj, boolean trim) {
		if (obj == null) {
			return false;
		}
		if (trim) {
			if (obj.toString().trim().length() == 0) {
				return false;
			}
		} else {
			if (obj.toString().length() == 0) {
				return false;
			}
		}
		
		return true;
	}

	/**
	 * Checks if is empty.
	 * 
	 * @param obj the obj
	 * 
	 * @return true, if is empty
	 */
	public static boolean isEmpty(Object obj) {
		if (obj == null) {
			return true;
		}
		if (obj.toString().length() == 0) {
			return true;
		}
		
		return false;
	}

	/**
	 * Checks if is not empty.
	 * 
	 * @param obj the obj
	 * 
	 * @return true, if is not empty
	 */
	public static boolean isNotEmpty(Object obj) {
		return isNotEmpty(obj, true);
	}

	/**
	 * obj의 문자열값 (toString() 호출)이 빈값인지를 검사 obj의 값이 null 일경우 true, obj의 값이 blank일
	 * 경우 true, obj의 값이 whitesapce 이고 trim이 true일 경우 true
	 * 
	 * @return boolean
	 * @param obj Object
	 * @param trim boolean
	 */
	public static boolean isEmpty(Object obj, boolean trim) {
		if (obj == null) {
			return true;
		}
		
		return trim ? isEmpty(obj.toString().trim()) : isEmpty(obj.toString());
	}

	// Defaults
	// -----------------------------------------------------------------------
	/**
	 * <p>Returns either the passed in String,
	 * or if the String is <code>null</code>, an empty String ("").</p>
	 * <p/>
	 * <pre>
	 * StringUtil.defaultString(null)  = ""
	 * StringUtil.defaultString("")    = ""
	 * StringUtil.defaultString("bat") = "bat"
	 * </pre>
	 *
	 * @param str the String to check, may be null
	 * @return the passed in String, or the empty String if it was <code>null</code>
	 * @see String#valueOf(Object)
	 */
	public static String defaultString(String str) {
		return str == null ? EMPTY : str;
	}

	/**
	 * <p>Returns either the passed in String, or if the String is
	 * <code>null</code>, the value of <code>defaultStr</code>.</p>
	 * <p/>
	 * <pre>
	 * StringUtil.defaultString(null, "NULL")  = "NULL"
	 * StringUtil.defaultString("", "NULL")    = ""
	 * StringUtil.defaultString("bat", "NULL") = "bat"
	 * </pre>
	 *
	 * @param str        the String to check, may be null
	 * @param defaultStr the default String to return if the input is <code>null</code>, may be null
	 * @return the passed in String, or the default if it was <code>null</code>
	 * @see String#valueOf(Object)
	 */
	public static String defaultString(String str, String defaultStr) {
		return str == null ? defaultStr : str;
	}

	/**
	 * <p>Removes control characters (char &lt;= 32) from both
	 * ends of this String, handling <code>null</code> by returning
	 * <code>null</code>.</p>
	 * <p/>
	 * <p>The String is trimmed using {@link String#trim()}.
	 * Trim removes start and end characters &lt;= 32.
	 * To strip whitespace use {@link #strip(String)}.</p>
	 * <p/>
	 * <p>To trim your choice of characters, use the
	 * {@link #strip(String, String)} methods.</p>
	 * <p/>
	 * <pre>
	 * StringUtil.trim(null)          = null
	 * StringUtil.trim("")            = ""
	 * StringUtil.trim("     ")       = ""
	 * StringUtil.trim("abc")         = "abc"
	 * StringUtil.trim("    abc    ") = "abc"
	 * </pre>
	 *
	 * @param str the String to be trimmed, may be null
	 * @return the trimmed string, <code>null</code> if null String input
	 */
	public static String trim(String str) {
		return str == null ? null : str.trim();
	}

	/**
	 * Split.
	 * 
	 * @param str the str
	 * @param separatorChars the separator chars
	 * 
	 * @return the string[]
	 */
	public static String[] split(String str, String separatorChars) {
		return splitWorker(str, separatorChars, -1, false);
	}

	/**
	 * Split worker.
	 * 
	 * @param str the str
	 * @param separatorChars the separator chars
	 * @param max the max
	 * @param preserveAllTokens the preserve all tokens
	 * 
	 * @return the string[]
	 */
	private static String[] splitWorker(String str, String separatorChars, int max, boolean preserveAllTokens) {
		// Performance tuned for 2.0 (JDK1.4)
		// Direct code is quicker than StringTokenizer.
		// Also, StringTokenizer uses isSpace() not isWhitespace()

		if (str == null) {
			return null;
		}
		
		int len = str.length();
		
		if (len == 0) {
			return new String[0];
		}
		
		List<String> list = new ArrayList<String>();
		int sizePlus1 = 1;
		int i = 0;
		int start = 0;
		boolean match = false;
		boolean lastMatch = false;
		
		if (separatorChars == null) {
			// Null separator means use whitespace
			while (i < len) {
				if (Character.isWhitespace(str.charAt(i))) {
					if (match || preserveAllTokens) {
						lastMatch = true;
						
						if (sizePlus1++ == max) {
							i = len;
							lastMatch = false;
						}
						
						list.add(str.substring(start, i));
						match = false;
					}
					
					start = ++i;
					continue;
				} else {
					lastMatch = false;
				}
				
				match = true;
				i++;
			}
		} else if (separatorChars.length() == 1) {
			// Optimise 1 character case
			char sep = separatorChars.charAt(0);
			
			while (i < len) {
				if (str.charAt(i) == sep) {
					if (match || preserveAllTokens) {
						lastMatch = true;
						
						if (sizePlus1++ == max) {
							i = len;
							lastMatch = false;
						}
						
						list.add(str.substring(start, i));
						match = false;
					}
					
					start = ++i;
					continue;
				} else {
					lastMatch = false;
				}
				
				match = true;
				i++;
			}
		} else {
			// standard case
			while (i < len) {
				if (separatorChars.indexOf(str.charAt(i)) >= 0) {
					if (match || preserveAllTokens) {
						lastMatch = true;
						
						if (sizePlus1++ == max) {
							i = len;
							lastMatch = false;
						}
						
						list.add(str.substring(start, i));
						match = false;
					}
					
					start = ++i;
					continue;
				} else {
					lastMatch = false;
				}
				
				match = true;
				i++;
			}
		}
		if (match || (preserveAllTokens && lastMatch)) {
			list.add(str.substring(start, i));
		}
		
		return (String[])list.toArray(new String[list.size()]);
	}

	/**
	 * <p>Deletes all whitespaces from a String as defined by
	 * {@link Character#isWhitespace(char)}.</p>
	 * <p/>
	 * <pre>
	 * StringUtil.deleteWhitespace(null)         = null
	 * StringUtil.deleteWhitespace("")           = ""
	 * StringUtil.deleteWhitespace("abc")        = "abc"
	 * StringUtil.deleteWhitespace("   ab  c  ") = "abc"
	 * </pre>
	 *
	 * @param str the String to delete whitespace from, may be null
	 * @return the String without whitespaces, <code>null</code> if null String input
	 */
	public static String deleteWhitespace(String str) {
		if (isEmpty(str)) {
			return str;
		}
		
		int sz = str.length();
		char[] chs = new char[sz];
		int count = 0;
		
		for (int i = 0; i < sz; i++) {
			if (!Character.isWhitespace(str.charAt(i))) {
				chs[count++] = str.charAt(i);
			}
		}
		
		
		if (count == sz) {
			return str;
		}
		
		return new String(chs, 0, count);
	}

	/**
	 * <p>Gets the substring after the first occurrence of a separator.
	 * The separator is not returned.</p>
	 * <p/>
	 * <p>A <code>null</code> string input will return <code>null</code>.
	 * An empty ("") string input will return the empty string.
	 * A <code>null</code> separator will return the empty string if the
	 * input string is not <code>null</code>.</p>
	 * <p/>
	 * <pre>
	 * StringUtil.substringAfter(null, *)      = null
	 * StringUtil.substringAfter("", *)        = ""
	 * StringUtil.substringAfter(*, null)      = ""
	 * StringUtil.substringAfter("abc", "a")   = "bc"
	 * StringUtil.substringAfter("abcba", "b") = "cba"
	 * StringUtil.substringAfter("abc", "c")   = ""
	 * StringUtil.substringAfter("abc", "d")   = ""
	 * StringUtil.substringAfter("abc", "")    = "abc"
	 * </pre>
	 *
	 * @param str       the String to get a substring from, may be null
	 * @param separator the String to search for, may be null
	 * @return the substring after the first occurrence of the separator, <code>null</code> if null String input
	 * @since 2.0
	 */
	public static String substringAfter(String str, String separator) {
		if (isEmpty(str)) {
			return str;
		}
		if (separator == null) {
			return EMPTY;
		}
		
		int pos = str.indexOf(separator);
		
		if (pos == -1) {
			return EMPTY;
		}
		
		return str.substring(pos + separator.length());
	}

	// Substring between
	// -----------------------------------------------------------------------
	/**
	 * <p>Gets the String that is nested in between two instances of the
	 * same String.</p>
	 * <p/>
	 * <p>A <code>null</code> input String returns <code>null</code>.
	 * A <code>null</code> tag returns <code>null</code>.</p>
	 * <p/>
	 * <pre>
	 * StringUtil.substringBetween(null, *)            = null
	 * StringUtil.substringBetween("", "")             = ""
	 * StringUtil.substringBetween("", "tag")          = null
	 * StringUtil.substringBetween("tagabctag", null)  = null
	 * StringUtil.substringBetween("tagabctag", "")    = ""
	 * StringUtil.substringBetween("tagabctag", "tag") = "abc"
	 * </pre>
	 *
	 * @param str the String containing the substring, may be null
	 * @param tag the String before and after the substring, may be null
	 * @return the substring, <code>null</code> if no match
	 * @since 2.0
	 */
	public static String substringBetween(String str, String tag) {
		return substringBetween(str, tag, tag);
	}

	/**
	 * <p>Gets the String that is nested in between two Strings.
	 * Only the first match is returned.</p>
	 * <p/>
	 * <p>A <code>null</code> input String returns <code>null</code>.
	 * A <code>null</code> open/close returns <code>null</code> (no match).
	 * An empty ("") open and close returns an empty string.</p>
	 * <p/>
	 * <pre>
	 * StringUtil.substringBetween("wx[b]yz", "[", "]") = "b"
	 * StringUtil.substringBetween(null, *, *)          = null
	 * StringUtil.substringBetween(*, null, *)          = null
	 * StringUtil.substringBetween(*, *, null)          = null
	 * StringUtil.substringBetween("", "", "")          = ""
	 * StringUtil.substringBetween("", "", "]")         = null
	 * StringUtil.substringBetween("", "[", "]")        = null
	 * StringUtil.substringBetween("yabcz", "", "")     = ""
	 * StringUtil.substringBetween("yabcz", "y", "z")   = "abc"
	 * StringUtil.substringBetween("yabczyabcz", "y", "z")   = "abc"
	 * </pre>
	 *
	 * @param str   the String containing the substring, may be null
	 * @param open  the String before the substring, may be null
	 * @param close the String after the substring, may be null
	 * @return the substring, <code>null</code> if no match
	 * @since 2.0
	 */
	public static String substringBetween(String str, String open, String close) {
		if (str == null || open == null || close == null) {
			return null;
		}
		
		int start = str.indexOf(open);
		
		if (start != -1) {
			int end = str.indexOf(close, start + open.length());
			
			if (end != -1) {
				return str.substring(start + open.length(), end);
			}
		}
		
		return null;
	}

	// SubStringAfter/SubStringBefore
	// -----------------------------------------------------------------------
	/**
	 * <p>Gets the substring before the first occurrence of a separator.
	 * The separator is not returned.</p>
	 * <p/>
	 * <p>A <code>null</code> string input will return <code>null</code>.
	 * An empty ("") string input will return the empty string.
	 * A <code>null</code> separator will return the input string.</p>
	 * <p/>
	 * <pre>
	 * StringUtil.substringBefore(null, *)      = null
	 * StringUtil.substringBefore("", *)        = ""
	 * StringUtil.substringBefore("abc", "a")   = ""
	 * StringUtil.substringBefore("abcba", "b") = "a"
	 * StringUtil.substringBefore("abc", "c")   = "ab"
	 * StringUtil.substringBefore("abc", "d")   = "abc"
	 * StringUtil.substringBefore("abc", "")    = ""
	 * StringUtil.substringBefore("abc", null)  = "abc"
	 * </pre>
	 *
	 * @param str       the String to get a substring from, may be null
	 * @param separator the String to search for, may be null
	 * @return the substring before the first occurrence of the separator, <code>null</code> if null String input
	 * @since 2.0
	 */
	public static String substringBefore(String str, String separator) {
		if (isEmpty(str) || separator == null) {
			return str;
		}
		if (separator.length() == 0) {
			return EMPTY;
		}
		
		int pos = str.indexOf(separator);
		
		if (pos == -1) {
			return str;
		}
		
		return str.substring(0, pos);
	}

	/**
	 * Replace module name.
	 * 
	 * @param moduleName the module name
	 * 
	 * @return the string
	 */
	public static String replaceModuleName(String moduleName) {
		ReplaceAllInfo replaceAllInfo = ReplaceAllInfo.getInstance();
		String prefix = "\\@\\{";
		String postfix = "\\}";

		Matcher matcher = Pattern.compile(prefix + ".*?" + postfix, Pattern.DOTALL).matcher(moduleName);

		StringBuilder sb = new StringBuilder();
		int pos = 0;
		String returnValue = moduleName;

		while (matcher.find()) {
			sb.append(moduleName.substring(pos, matcher.start()));
			pos = matcher.start();
			String tmp = moduleName.substring(matcher.start(), matcher.end()).replaceAll(prefix, "").replaceAll(postfix, "");
			
			if (tmp != null) {
				String value = replaceAllInfo.getReplaceInfo(tmp.trim());
				
				if (value == null) {
					sb.append(tmp.trim());
				} else {
					sb.append(value);
				}
				
				sb.append(moduleName.substring(matcher.end()));
				returnValue = sb.toString();
			}
		}

		log.debug("origin module name : " + moduleName + ", returnValue : " + returnValue);
		return returnValue;
	}

	/**
	 * Replace page src.
	 * 
	 * @param pagesrc the pagesrc
	 * 
	 * @return the string
	 */
	public static String replacePageSrc(String pagesrc) {
		ReplaceAllInfo replaceAllInfo = ReplaceAllInfo.getInstance();
		String prefix = "\\@\\{";
		String postfix = "\\}";

		Matcher matcher = Pattern.compile(prefix + ".*?" + postfix, Pattern.DOTALL).matcher(pagesrc);

		StringBuilder sb = new StringBuilder();
		int pos = 0;

		while (matcher.find()) {
			sb.append(pagesrc.substring(pos, matcher.start()));
			pos = matcher.start();
			String tmp = pagesrc.substring(matcher.start(), matcher.end()).replaceAll(prefix, "").replaceAll(postfix, "");
			
			if (tmp != null) {
				String value = replaceAllInfo.getReplaceInfo(tmp.trim());
				
				if (value == null) {
					sb.append(tmp);
				} else {
					sb.append(value);
				}
			}
			
			
			pos = matcher.end();
		}
		
		sb.append(pagesrc.substring(pos, pagesrc.length()));

		log.debug("origin page src : " + pagesrc + ", returnValue : " + sb.toString());
		return sb.toString();
	}

    /**
     * src에 정의된 변수를 map에 있는 내용으로 교체하여 새로운 src반환.
     *
     * @param src String
     * @param map Map
     * @return String
     */
    public static String getNewSrc(String src, Map<String, Object> map) {
        String prefix = "\\$\\{";
        String postfix = "\\}";

        Matcher matcher = Pattern.compile(prefix + ".*?" + postfix, Pattern.DOTALL).matcher(src);

        StringBuilder sb = new StringBuilder();
        int pos = 0;

        while (matcher.find()) {
            sb.append(src.substring(pos, matcher.start()));
            pos = matcher.start();
            String tmp = src.substring(matcher.start(), matcher.end()).replaceAll(prefix, "").replaceAll(postfix, "");

            if (tmp != null) {
                try {
                    tmp = OgnlUtil.evaluateAndGet(tmp, map);
                } catch (Exception e) {
                    log.error("Cannot replace src \r\nKey[" + tmp + "]\r\nValue[\r\n" + map.toString()
                            + "\r\n]");
                }
            }

            sb.append(tmp);
            pos = matcher.end();
        }

        sb.append(src.substring(pos, src.length()));
        return sb.toString();
    }
}
